/*
 * @文件描述:
 * 管理socket相关,包括创建服务器,接受来自客户端的连接请求,以及解析客户端发来的数据,并发送相应的数据给客户端
 * 比如客户端(即语音控制程序)请求获取电机的实时位置信息,将由此进行接受并解析数据,之后自动发送相应的电机位置给客户端
 * 发送和接受的原始数据均为string,string的格式定义也在此.
   为了灵活处理,电机的控制函数不能写死在这个里面,采用回调函数的方式设置电机的控制函数和信息获取函数
 * @版本:
 * @作者:周晨阳
 * @Date:2021-09-25 10:15:03
 */

using System.Net.Sockets;
using System.Net;
using System.Diagnostics;
using System;
using System.Text;
using System.Linq;

namespace Motor_Control
{
    /// <summary>
    /// socket连接状态枚举
    /// </summary>
    public enum MY_SOCKET_CONNECT_STATE
    {
        INITIALIZING = 0,
        CONNECTING = 1,
        DISCONNECT = 2,
        CONNECTION_FAILED = 3,
        CONNECTED = 4,
    }
    // State object for reading client data asynchronously  

    /// <summary>
    /// 管理socket连接,数据发送,接收等底层工作
    /// </summary>
    public class MySocket
    {
        /// <summary>
        /// 用于socket通讯时的一些参数传递
        /// </summary>
        private class StateObject
        {
            // Size of receive buffer.  
            public const int BufferSize = 1024;

            // Receive buffer.  
            public byte[] buffer = new byte[BufferSize];

            // Received data string.
            public StringBuilder sb = new StringBuilder();

            // Client socket.
            public Socket workSocket = null;
        }
        private static Socket workingSocket;
        private MY_SOCKET_CONNECT_STATE connectionState = MY_SOCKET_CONNECT_STATE.INITIALIZING;
        private MotorControlView view;
        public MySocket()
        {
        }
        /// <summary>
        /// 创建socket实例
        /// </summary>
        /// <param name="view">view对象</param>
        public MySocket(MotorControlView view)
        {
            this.view = view;

        }
        /// <summary>
        /// 绑定view对象
        /// </summary>
        /// <param name="view"></param>
        public void bindView(MotorControlView view)
        {
            this.view = view;
        }


        /// <summary>
        /// 初始化socket,用于和语音识别对接，异步方式的socket，不会堵塞线程
        /// </summary>
        public void StartServerAsync()
        {
            connectionState = MY_SOCKET_CONNECT_STATE.CONNECTING;
            view.onViewSetUnityConnection(connectionState);
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //本机IP:192.168.0.103或者输入127.0.0.1
            //IPAddress xxx.xx.xx.xx IPEndPoint xxx.xx.xx.xx:port
            //IPAddress ipAddress = new IPAddress(new byte[] {192, 168, 0, 103 });
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 88);
            serverSocket.Bind(ipEndPoint);//绑定IP和端口号
            serverSocket.Listen(0);//开始监听端口号

            serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);

        }
        /// <summary>
        /// 获取socket连接状态
        /// </summary>
        /// <returns></returns>
        public MY_SOCKET_CONNECT_STATE getSocketState()
        {
            return connectionState;
        }
        /// <summary>
        /// 获取可以用的socket对象
        /// </summary>
        /// <returns></returns>
        public static Socket getWorkingSocket()
        {
            return workingSocket;
        }
        /// <summary>
        /// 当客户端连接到服务器时，会异步调用此函数
        /// </summary>
        /// <param name="ar"></param>
        private void AcceptCallBack(IAsyncResult ar)
        {

            Socket listener = ar.AsyncState as Socket;

            //这个handler用于处理服务器和客户端之间的操作
            Socket handler = listener.EndAccept(ar);

            Console.WriteLine("连接成功");
            connectionState = MY_SOCKET_CONNECT_STATE.CONNECTED;
            view.onViewSetUnityConnection(connectionState);


            // Create the state object.  
            StateObject stateObject = new StateObject();
            stateObject.workSocket = handler;
            workingSocket = handler;
            //开始接受来自客户端的数据
            handler.BeginReceive(stateObject.buffer, 0, StateObject.BufferSize, 0,
            new AsyncCallback(ReadCallback), stateObject);//从已连接的Socket中开始异步地接收数据

            //每次连接了客户端后会向客户端发送一个测试字符串,以测试连接是否成功
            // Send(getWorkingSocket(), "This is a test text from server,you should see this after started client,otherwise the connection is wrong");
            // Send(getWorkingSocket(), "Server Sending time:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff"));
            // Send(getWorkingSocket(), "Request=1;Type=1;Count=1;Value[0]=123;");
            //此段语句的作用是继续接受更多来自客户端的连接,或者重新连接
            listener.BeginAccept(new AsyncCallback(AcceptCallBack), listener);

        }

        /// <summary>
        /// 接收到数据后的回调函数,用于处理数据
        /// </summary>
        /// <param name="ar"></param>
        private void ReadCallback(IAsyncResult ar)
        {
            String content = String.Empty;

            // Retrieve the state object and the handler socket  
            // from the asynchronous state object.  
            StateObject state = (StateObject)ar.AsyncState;
            Socket handler = state.workSocket;
            try
            {
                // Read data from the client socket.
                int bytesRead = handler.EndReceive(ar);

                if (bytesRead > 0)
                {
                    // There  might be more data, so store the data received so far.  
                    state.sb.Clear();
                    state.sb.Append(Encoding.UTF8.GetString(
                        state.buffer, 0, bytesRead));
                    // Check for end-of-file tag. If it is not there, read
                    // more data.  
                    content = state.sb.ToString();
                    if (content.IndexOf("<EOF>") > -1)
                    {
                        // All the data has been read from the
                        // client. Display it on the console.  
                        // Console.WriteLine("Read {0} bytes from client. \n Data : {1}",
                        //     content.Length, content);
                        string[] stringSeparators = new string[] { "<EOF>" };
                        string[] strings = content.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries);
                        //到这里加入解析字符串的步骤
                        for (int i = 0; i < strings.Length; i++)
                        {
                            //到这里加入解析字符串的步骤
                            Analysis.analysisString(strings[i]);
                        }



                        //重复接受来自客户端的数据
                        state.sb.Clear();
                        state.buffer = new byte[StateObject.BufferSize];
                        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReadCallback), state);//从已连接的Socket中开始异步地接收数据
                    }
                    else
                    {
                        // Not all data received. Get more.  
                        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReadCallback), state);
                    }
                }
            }
            catch (System.Exception e)
            {
                Console.WriteLine(e);
                connectionState = MY_SOCKET_CONNECT_STATE.CONNECTION_FAILED;
                view.onViewSetUnityConnection(connectionState);

            }

        }



        /// <summary>
        /// 向客户端发送数据
        /// </summary>
        /// <param name="handler"> socket handler</param>
        /// <param name="data">要发送的数据,只能是字符串</param>
        public static void Send(Socket handler, String data)
        {
            // Convert the string data to byte data using ASCII encoding.  
            byte[] byteData = Encoding.UTF8.GetBytes(data + "<EOF>");
            // Begin sending the data to the remote device.  
            handler.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), handler);
        }

        /// <summary>
        /// 发送数据的回调函数,用于指示发送是否成功
        /// </summary>
        /// <param name="ar"></param>
        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.  
                Socket handler = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.  
                int bytesSent = handler.EndSend(ar);
                //Console.WriteLine("Sent {0} bytes to client.", bytesSent);

                //handler.Shutdown(SocketShutdown.Both);
                //handler.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

    }
}